home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / manageme / tcpdump-.001 / tcpdump-~ / tcpdump-3.0.2-linux / tcpdump-3.0.2 / print-nfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-12  |  18.8 KB  |  863 lines

  1. /*
  2.  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that: (1) source code distributions
  7.  * retain the above copyright notice and this paragraph in its entirety, (2)
  8.  * distributions including binary code include the above copyright notice and
  9.  * this paragraph in its entirety in the documentation or other materials
  10.  * provided with the distribution, and (3) all advertising materials mentioning
  11.  * features or use of this software display the following acknowledgement:
  12.  * ``This product includes software developed by the University of California,
  13.  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
  14.  * the University nor the names of its contributors may be used to endorse
  15.  * or promote products derived from this software without specific prior
  16.  * written permission.
  17.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  18.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  19.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  20.  */
  21.  
  22. #ifndef lint
  23. static char rcsid[] =
  24.     "@(#) $Header: print-nfs.c,v 1.41 94/06/12 14:35:15 leres Exp $ (LBL)";
  25. #endif
  26.  
  27. #include <sys/param.h>
  28. #include <sys/time.h>
  29. #include <sys/types.h>
  30. #include <sys/socket.h>
  31.  
  32. #include <net/if.h>
  33.  
  34. #include <netinet/in.h>
  35. #include <netinet/if_ether.h>
  36. #include <netinet/in_systm.h>
  37. #include <netinet/ip.h>
  38. #include <netinet/ip_var.h>
  39.  
  40. #ifdef SOLARIS
  41. #include <tiuser.h>
  42. #endif
  43. #include <rpc/types.h>
  44. #include <rpc/auth.h>
  45. #include <rpc/auth_unix.h>
  46. #include <rpc/svc.h>
  47. #include <rpc/xdr.h>
  48. #include <rpc/rpc_msg.h>
  49.  
  50. #include <ctype.h>
  51. #include <stdio.h>
  52. #include <errno.h>
  53. #include <string.h>
  54.  
  55. #include "interface.h"
  56. #include "addrtoname.h"
  57. #include "extract.h"            /* must come after interface.h */
  58.  
  59. #include "nfsv2.h"
  60. #include "nfsfh.h"
  61.  
  62. static void nfs_printfh(const u_int32 *);
  63. static void xid_map_enter(const struct rpc_msg *, const struct ip *);
  64. static int32 xid_map_find(const struct rpc_msg *, const struct ip *);
  65. static void interp_reply(const struct rpc_msg *, u_int32, int);
  66.  
  67. void
  68. nfsreply_print(register const u_char *bp, int length,
  69.            register const u_char *bp2)
  70. {
  71.     register const struct rpc_msg *rp;
  72.     register const struct ip *ip;
  73.     int32 proc;
  74.  
  75.     rp = (const struct rpc_msg *)bp;
  76.     ip = (const struct ip *)bp2;
  77.  
  78.     if (!nflag)
  79.         (void)printf("%s.nfs > %s.%x: reply %s %d",
  80.                  ipaddr_string(&ip->ip_src),
  81.                  ipaddr_string(&ip->ip_dst),
  82.                  ntohl(rp->rm_xid),
  83.                  ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
  84.                      "ok":"ERR",
  85.                  length);
  86.     else
  87.         (void)printf("%s.%x > %s.%x: reply %s %d",
  88.                  ipaddr_string(&ip->ip_src),
  89.                  NFS_PORT,
  90.                  ipaddr_string(&ip->ip_dst),
  91.                  ntohl(rp->rm_xid),
  92.                  ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
  93.                      "ok":"ERR",
  94.                  length);
  95.  
  96.     proc = xid_map_find(rp, ip);
  97.     if (proc >= 0)
  98.         interp_reply(rp, (u_int32)proc, length);
  99. }
  100.  
  101. /*
  102.  * Return a pointer to the first file handle in the packet.
  103.  * If the packet was truncated, return 0.
  104.  */
  105. static const u_int32 *
  106. parsereq(register const struct rpc_msg *rp, register int length)
  107. {
  108.     register const u_int32 *dp = (u_int32 *)&rp->rm_call.cb_cred;
  109.     register const u_int32 *ep = (u_int32 *)snapend;
  110.     register u_int len;
  111.  
  112.     if (&dp[2] >= ep)
  113.         return (0);
  114.     /*
  115.      * find the start of the req data (if we captured it)
  116.      */
  117.     len =  ntohl(dp[1]);
  118.     if (dp < ep && len < length) {
  119.         dp += (len + (2 * sizeof(u_int32) + 3)) / sizeof(u_int32);
  120.         len = ntohl(dp[1]);
  121.         if ((dp < ep) && (len < length)) {
  122.             dp += (len + (2 * sizeof(u_int32) + 3)) /
  123.                 sizeof(u_int32);
  124.             if (dp < ep)
  125.                 return (dp);
  126.         }
  127.     }
  128.     return (0);
  129. }
  130.  
  131. /*
  132.  * Print out an NFS file handle and return a pointer to following word.
  133.  * If packet was truncated, return 0.
  134.  */
  135. static const u_int32 *
  136. parsefh(register const u_int32 *dp)
  137. {
  138.     if (dp + 8 <= (u_int32 *)snapend) {
  139.         nfs_printfh(dp);
  140.         return (dp + 8);
  141.     }
  142.     return (0);
  143. }
  144.  
  145. /*
  146.  * Print out a file name and return pointer to 32-bit word past it.
  147.  * If packet was truncated, return 0.
  148.  */
  149. static const u_int32 *
  150. parsefn(register const u_int32 *dp)
  151. {
  152.     register u_int32 len;
  153.     register const u_char *cp;
  154.  
  155.     /* Bail if we don't have the string length */
  156.     if ((u_char *)dp > snapend - sizeof(*dp))
  157.         return(0);
  158.  
  159.     /* Fetch string length; convert to host order */
  160.     len = *dp++;
  161.     NTOHL(len);
  162.  
  163.     cp = (u_char *)dp;
  164.     /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
  165.     dp += ((len + 3) & ~3) / sizeof(*dp);
  166.     if ((u_char *)dp > snapend)
  167.         return (0);
  168.     /* XXX seems like we should be checking the length */
  169.     (void) fn_printn(cp, len, NULL);
  170.  
  171.     return (dp);
  172. }
  173.  
  174. /*
  175.  * Print out file handle and file name.
  176.  * Return pointer to 32-bit word past file name.
  177.  * If packet was truncated (or there was some other error), return 0.
  178.  */
  179. static const u_int32 *
  180. parsefhn(register const u_int32 *dp)
  181. {
  182.     dp = parsefh(dp);
  183.     if (dp == 0)
  184.         return (0);
  185.     putchar(' ');
  186.     return (parsefn(dp));
  187. }
  188.  
  189. void
  190. nfsreq_print(register const u_char *bp, int length, register const u_char *bp2)
  191. {
  192.     register const struct rpc_msg *rp;
  193.     register const struct ip *ip;
  194.     register const u_int32 *dp;
  195.     register const u_char *ep;
  196.  
  197. #define TCHECK(p, l) if ((u_char *)(p) > ep - l) break
  198.  
  199.     rp = (const struct rpc_msg *)bp;
  200.     ip = (const struct ip *)bp2;
  201.     ep = snapend;
  202.     if (!nflag)
  203.         (void)printf("%s.%x > %s.nfs: %d",
  204.                  ipaddr_string(&ip->ip_src),
  205.                  ntohl(rp->rm_xid),
  206.                  ipaddr_string(&ip->ip_dst),
  207.                  length);
  208.     else
  209.         (void)printf("%s.%x > %s.%x: %d",
  210.                  ipaddr_string(&ip->ip_src),
  211.                  ntohl(rp->rm_xid),
  212.                  ipaddr_string(&ip->ip_dst),
  213.                  NFS_PORT,
  214.                  length);
  215.  
  216.     xid_map_enter(rp, ip);    /* record proc number for later on */
  217.  
  218.     switch (ntohl(rp->rm_call.cb_proc)) {
  219. #ifdef NFSPROC_NOOP
  220.     case NFSPROC_NOOP:
  221.         printf(" nop");
  222.         return;
  223. #else
  224. #define NFSPROC_NOOP -1
  225. #endif
  226.     case NFSPROC_NULL:
  227.         printf(" null");
  228.         return;
  229.  
  230.     case NFSPROC_GETATTR:
  231.         printf(" getattr");
  232.         if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  233.             return;
  234.         break;
  235.  
  236.     case NFSPROC_SETATTR:
  237.         printf(" setattr");
  238.         if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  239.             return;
  240.         break;
  241.  
  242. #if NFSPROC_ROOT != NFSPROC_NOOP
  243.     case NFSPROC_ROOT:
  244.         printf(" root");
  245.         break;
  246. #endif
  247.     case NFSPROC_LOOKUP:
  248.         printf(" lookup");
  249.         if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  250.             return;
  251.         break;
  252.  
  253.     case NFSPROC_READLINK:
  254.         printf(" readlink");
  255.         if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  256.             return;
  257.         break;
  258.  
  259.     case NFSPROC_READ:
  260.         printf(" read");
  261.         if ((dp = parsereq(rp, length)) != 0 &&
  262.             (dp = parsefh(dp)) != 0) {
  263.             TCHECK(dp, 3 * sizeof(*dp));
  264.             printf(" %lu bytes @ %lu",
  265.                    ntohl(dp[1]), ntohl(dp[0]));
  266.             return;
  267.         }
  268.         break;
  269.  
  270. #if NFSPROC_WRITECACHE != NFSPROC_NOOP
  271.     case NFSPROC_WRITECACHE:
  272.         printf(" writecache");
  273.         if ((dp = parsereq(rp, length)) != 0 &&
  274.             (dp = parsefh(dp)) != 0) {
  275.             TCHECK(dp, 4 * sizeof(*dp));
  276.             printf(" %lu (%lu) bytes @ %lu (%lu)",
  277.                    ntohl(dp[3]), ntohl(dp[2]),
  278.                    ntohl(dp[1]), ntohl(dp[0]));
  279.             return;
  280.         }
  281.         break;
  282. #endif
  283.     case NFSPROC_WRITE:
  284.         printf(" write");
  285.         if ((dp = parsereq(rp, length)) != 0 &&
  286.             (dp = parsefh(dp)) != 0) {
  287.             TCHECK(dp, 4 * sizeof(*dp));
  288.             printf(" %lu (%lu) bytes @ %lu (%lu)",
  289.                    ntohl(dp[3]), ntohl(dp[2]),
  290.                    ntohl(dp[1]), ntohl(dp[0]));
  291.             return;
  292.         }
  293.         break;
  294.  
  295.     case NFSPROC_CREATE:
  296.         printf(" create");
  297.         if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  298.             return;
  299.         break;
  300.  
  301.     case NFSPROC_REMOVE:
  302.         printf(" remove");
  303.         if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  304.             return;
  305.         break;
  306.  
  307.     case NFSPROC_RENAME:
  308.         printf(" rename");
  309.         if ((dp = parsereq(rp, length)) != 0 &&
  310.             (dp = parsefhn(dp)) != 0) {
  311.             fputs(" ->", stdout);
  312.             if (parsefhn(dp) != 0)
  313.                 return;
  314.         }
  315.         break;
  316.  
  317.     case NFSPROC_LINK:
  318.         printf(" link");
  319.         if ((dp = parsereq(rp, length)) != 0 &&
  320.             (dp = parsefh(dp)) != 0) {
  321.             fputs(" ->", stdout);
  322.             if (parsefhn(dp) != 0)
  323.                 return;
  324.         }
  325.         break;
  326.  
  327.     case NFSPROC_SYMLINK:
  328.         printf(" symlink");
  329.         if ((dp = parsereq(rp, length)) != 0 &&
  330.             (dp = parsefhn(dp)) != 0) {
  331.             fputs(" -> ", stdout);
  332.             if (parsefn(dp) != 0)
  333.                 return;
  334.         }
  335.         break;
  336.  
  337.     case NFSPROC_MKDIR:
  338.         printf(" mkdir");
  339.         if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  340.             return;
  341.         break;
  342.  
  343.     case NFSPROC_RMDIR:
  344.         printf(" rmdir");
  345.         if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
  346.             return;
  347.         break;
  348.  
  349.     case NFSPROC_READDIR:
  350.         printf(" readdir");
  351.         if ((dp = parsereq(rp, length)) != 0 &&
  352.             (dp = parsefh(dp)) != 0) {
  353.             TCHECK(dp, 2 * sizeof(*dp));
  354.             /*
  355.              * Print the offset as signed, since -1 is common,
  356.              * but offsets > 2^31 aren't.
  357.              */
  358.             printf(" %lu bytes @ %ld", ntohl(dp[1]), ntohl(dp[0]));
  359.             return;
  360.         }
  361.         break;
  362.  
  363.     case NFSPROC_STATFS:
  364.         printf(" statfs");
  365.         if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
  366.             return;
  367.         break;
  368.  
  369.     default:
  370.         printf(" proc-%lu", ntohl(rp->rm_call.cb_proc));
  371.         return;
  372.     }
  373.     fputs(" [|nfs]", stdout);
  374. #undef TCHECK
  375. }
  376.  
  377. /*
  378.  * Print out an NFS file handle.
  379.  * We assume packet was not truncated before the end of the
  380.  * file handle pointed to by dp.
  381.  *
  382.  * Note: new version (using portable file-handle parser) doesn't produce
  383.  * generation number.  It probably could be made to do that, with some
  384.  * additional hacking on the parser code.
  385.  */
  386. static void
  387. nfs_printfh(register const u_int32 *dp)
  388. {
  389.     my_fsid fsid;
  390.     ino_t ino;
  391.     char *sfsname = NULL;
  392.  
  393.     Parse_fh((caddr_t*)dp, &fsid, &ino, NULL, &sfsname, 0);
  394.  
  395.     if (sfsname) {
  396.         /* file system ID is ASCII, not numeric, for this server OS */
  397.         static char temp[NFS_FHSIZE+1];
  398.  
  399.         /* Make sure string is null-terminated */
  400.         strncpy(temp, sfsname, NFS_FHSIZE);
  401.         /* Remove trailing spaces */
  402.         sfsname = strchr(temp, ' ');
  403.         if (sfsname)
  404.         *sfsname = 0;
  405.  
  406.         (void)printf(" fh %s/%ld", temp, ino);
  407.     }
  408.     else {
  409.         (void)printf(" fh %d,%d/%ld",
  410.             fsid.fsid_dev.Major, fsid.fsid_dev.Minor,
  411.             ino);
  412.     }
  413. }
  414.  
  415. /*
  416.  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
  417.  * us to match up replies with requests and thus to know how to parse
  418.  * the reply.
  419.  */
  420.  
  421. struct xid_map_entry {
  422.     u_int32        xid;        /* transaction ID (net order) */
  423.     struct in_addr    client;        /* client IP address (net order) */
  424.     struct in_addr    server;        /* server IP address (net order) */
  425.     u_int32        proc;        /* call proc number (host order) */
  426. };
  427.  
  428. /*
  429.  * Map entries are kept in an array that we manage as a ring;
  430.  * new entries are always added at the tail of the ring.  Initially,
  431.  * all the entries are zero and hence don't match anything.
  432.  */
  433.  
  434. #define    XIDMAPSIZE    64
  435.  
  436. struct xid_map_entry xid_map[XIDMAPSIZE];
  437.  
  438. int    xid_map_next = 0;
  439. int    xid_map_hint = 0;
  440.  
  441. static void
  442. xid_map_enter(const struct rpc_msg *rp, const struct ip *ip)
  443. {
  444.     struct xid_map_entry *xmep;
  445.  
  446.     xmep = &xid_map[xid_map_next];
  447.  
  448.     if (++xid_map_next >= XIDMAPSIZE)
  449.         xid_map_next = 0;
  450.  
  451.     xmep->xid = rp->rm_xid;
  452.     xmep->client = ip->ip_src;
  453.     xmep->server = ip->ip_dst;
  454.     xmep->proc = ntohl(rp->rm_call.cb_proc);
  455. }
  456.  
  457. /* Returns NFSPROC_xxx or -1 on failure */
  458. static int32
  459. xid_map_find(const struct rpc_msg *rp, const struct ip *ip)
  460. {
  461.     int i;
  462.     struct xid_map_entry *xmep;
  463.     u_int32 xid = rp->rm_xid;
  464.     u_int32 clip = ip->ip_dst.s_addr;
  465.     u_int32 sip = ip->ip_src.s_addr;
  466.  
  467.     /* Start searching from where we last left off */
  468.     i = xid_map_hint;
  469.     do {
  470.         xmep = &xid_map[i];
  471.         if (xmep->xid == xid && xmep->client.s_addr == clip &&
  472.             xmep->server.s_addr == sip) {
  473.             /* match */
  474.             xid_map_hint = i;
  475.             return ((int32)xmep->proc);
  476.         }
  477.         if (++i >= XIDMAPSIZE)
  478.             i = 0;
  479.     } while (i != xid_map_hint);
  480.  
  481.     /* search failed */
  482.     return(-1);
  483. }
  484.  
  485. /*
  486.  * Routines for parsing reply packets
  487.  */
  488.  
  489. /*
  490.  * Return a pointer to the beginning of the actual results.
  491.  * If the packet was truncated, return 0.
  492.  */
  493. static const u_int32 *
  494. parserep(register const struct rpc_msg *rp, register int length)
  495. {
  496.     register const u_int32 *dp;
  497.     register const u_int32 *ep = (const u_int32 *)snapend;
  498.     int len;
  499.     enum accept_stat astat;
  500.  
  501.     /*
  502.      * Portability note:
  503.      * Here we find the address of the ar_verf credentials.
  504.      * Originally, this calculation was
  505.      *    dp = (u_int32 *)&rp->rm_reply.rp_acpt.ar_verf
  506.      * On the wire, the rp_acpt field starts immediately after
  507.      * the (32 bit) rp_stat field.  However, rp_acpt (which is a
  508.      * "struct accepted_reply") contains a "struct opaque_auth",
  509.      * whose internal representation contains a pointer, so on a
  510.      * 64-bit machine the compiler inserts 32 bits of padding
  511.      * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
  512.      * the internal representation to parse the on-the-wire
  513.      * representation.  Instead, we skip past the rp_stat field,
  514.      * which is an "enum" and so occupies one 32-bit word.
  515.      */
  516.     dp = ((const u_int32 *)&rp->rm_reply) + 1;
  517.     if (&dp[1] >= ep)
  518.         return(0);
  519.     len = ntohl(dp[1]);
  520.     if (len >= length)
  521.         return(0);
  522.     /*
  523.      * skip past the ar_verf credentials.
  524.      */
  525.     dp += (len + (2*sizeof(u_int32) + 3)) / sizeof(u_int32);
  526.     if (dp >= ep)
  527.         return(0);
  528.  
  529.     /*
  530.      * now we can check the ar_stat field
  531.      */
  532.     astat = ntohl(*(enum accept_stat *)dp);
  533.     switch (astat) {
  534.  
  535.     case SUCCESS:
  536.         break;
  537.  
  538.     case PROG_UNAVAIL:
  539.         printf(" PROG_UNAVAIL");
  540.         return(0);
  541.  
  542.     case PROG_MISMATCH:
  543.         printf(" PROG_MISMATCH");
  544.         return(0);
  545.  
  546.     case PROC_UNAVAIL:
  547.         printf(" PROC_UNAVAIL");
  548.         return(0);
  549.  
  550.     case GARBAGE_ARGS:
  551.         printf(" GARBAGE_ARGS");
  552.         return(0);
  553.  
  554.     case SYSTEM_ERR:
  555.         printf(" SYSTEM_ERR");
  556.         return(0);
  557.  
  558.     default:
  559.         printf(" ar_stat %d", astat);
  560.         return(0);
  561.     }
  562.     /* successful return */
  563.     if ((sizeof(astat) + ((char *)dp)) < (char *)ep)
  564.         return((u_int32 *) (sizeof(astat) + ((char *)dp)));
  565.  
  566.     return (0);
  567. }
  568.  
  569. #define T2CHECK(p, l) if ((u_char *)(p) > ((u_char *)snapend) - l) return(0)
  570.  
  571. /*
  572.  * Not all systems have strerror().
  573.  */
  574. static char *
  575. strerr(int errno)
  576. {
  577.     extern int sys_nerr;
  578.     extern char *sys_errlist[];
  579.  
  580.     if (errno < sys_nerr)
  581.         return (sys_errlist[errno]);
  582.     return (0);
  583. }
  584.  
  585. static const u_int32 *
  586. parsestatus(const u_int32 *dp)
  587. {
  588.     int errno;
  589.     T2CHECK(dp, 4);
  590.  
  591.     errno = ntohl(dp[0]);
  592.     if (errno != 0) {
  593.         char *errmsg;
  594.  
  595.         if (qflag)
  596.             return(0);
  597.  
  598.         errmsg = strerr(errno);
  599.         if (errmsg)
  600.             printf(" ERROR: %s", errmsg);
  601.         else
  602.             printf(" ERROR: %d", errno);
  603.         return(0);
  604.     }
  605.     return (dp + 1);
  606. }
  607.  
  608. static struct token type2str[] = {
  609.     { NFNON,    "NON" },
  610.     { NFREG,    "REG" },
  611.     { NFDIR,    "DIR" },
  612.     { NFBLK,    "BLK" },
  613.     { NFCHR,    "CHR" },
  614.     { NFLNK,    "LNK" },
  615.     { 0,        NULL }
  616. };
  617.  
  618. static const u_int32 *
  619. parsefattr(const u_int32 *dp, int verbose)
  620. {
  621.     const struct nfsv2_fattr *fap;
  622.  
  623.     T2CHECK(dp, 4);
  624.  
  625.     fap = (const struct nfsv2_fattr *)dp;
  626.     if (verbose)
  627.         printf(" %s %o ids %d/%d sz %d ",
  628.             tok2str(type2str, "unk-ft %d ", ntohl(fap->fa_type)),
  629.                ntohl(fap->fa_mode), ntohl(fap->fa_uid),
  630.                ntohl(fap->fa_gid), ntohl(fap->fa_nfssize));
  631.     /* print lots more stuff */
  632.     if (verbose > 1) {
  633.         printf("nlink %d rdev %x fsid %x nodeid %x a/m/ctime ",
  634.                ntohl(fap->fa_nlink), ntohl(fap->fa_nfsrdev),
  635.                ntohl(fap->fa_nfsfsid), ntohl(fap->fa_nfsfileid));
  636.         printf("%d.%06d ",
  637.                ntohl(fap->fa_nfsatime.nfs_sec),
  638.                ntohl(fap->fa_nfsatime.nfs_usec));
  639.         printf("%d.%06d ",
  640.                ntohl(fap->fa_nfsmtime.nfs_sec),
  641.                ntohl(fap->fa_nfsmtime.nfs_usec));
  642.         printf("%d.%06d ",
  643.                ntohl(fap->fa_nfsctime.nfs_sec),
  644.                ntohl(fap->fa_nfsctime.nfs_usec));
  645.     }
  646.     return ((const u_int32 *)&fap[1]);
  647. }
  648.  
  649. static int
  650. parseattrstat(const u_int32 *dp, int verbose)
  651. {
  652.     dp = parsestatus(dp);
  653.     if (dp == NULL)
  654.         return (0);
  655.  
  656.     return ((int)parsefattr(dp, verbose));
  657. }
  658.  
  659. static int
  660. parsediropres(const u_int32 *dp)
  661. {
  662.     dp = parsestatus(dp);
  663.     if (dp == NULL)
  664.         return (0);
  665.  
  666.     dp = parsefh(dp);
  667.     if (dp == NULL)
  668.         return (0);
  669.  
  670.     return (parsefattr(dp, vflag) != NULL);
  671. }
  672.  
  673. static int
  674. parselinkres(const u_int32 *dp)
  675. {
  676.     dp = parsestatus(dp);
  677.     if (dp == NULL)
  678.         return(0);
  679.  
  680.     putchar(' ');
  681.     return (parsefn(dp) != NULL);
  682. }
  683.  
  684. static int
  685. parsestatfs(const u_int32 *dp)
  686. {
  687.     const struct nfsv2_statfs *sfsp;
  688.  
  689.     dp = parsestatus(dp);
  690.     if (dp == NULL)
  691.         return(0);
  692.  
  693.     if (qflag)
  694.         return(1);
  695.  
  696.     T2CHECK(dp, 20);
  697.  
  698.     sfsp = (const struct nfsv2_statfs *)dp;
  699.     printf(" tsize %d bsize %d blocks %d bfree %d bavail %d",
  700.            ntohl(sfsp->sf_tsize), ntohl(sfsp->sf_bsize),
  701.            ntohl(sfsp->sf_blocks), ntohl(sfsp->sf_bfree),
  702.            ntohl(sfsp->sf_bavail));
  703.  
  704.     return (1);
  705. }
  706.  
  707. static int
  708. parserddires(const u_int32 *dp)
  709. {
  710.     dp = parsestatus(dp);
  711.     if (dp == 0)
  712.         return (0);
  713.     if (qflag)
  714.         return (1);
  715.  
  716.     T2CHECK(dp, 12);
  717.     printf(" offset %x size %d ", ntohl(dp[0]), ntohl(dp[1]));
  718.     if (dp[2] != 0)
  719.         printf("eof");
  720.  
  721.     return (1);
  722. }
  723.  
  724. static void
  725. interp_reply(const struct rpc_msg *rp, u_int32 proc, int length)
  726. {
  727.     register const u_int32 *dp;
  728.  
  729.     switch (proc) {
  730.  
  731. #ifdef NFSPROC_NOOP
  732.     case NFSPROC_NOOP:
  733.         printf(" nop");
  734.         return;
  735. #else
  736. #define NFSPROC_NOOP -1
  737. #endif
  738.     case NFSPROC_NULL:
  739.         printf(" null");
  740.         return;
  741.  
  742.     case NFSPROC_GETATTR:
  743.         printf(" getattr");
  744.         dp = parserep(rp, length);
  745.         if (dp != 0 && parseattrstat(dp, !qflag) != 0)
  746.             return;
  747.         break;
  748.  
  749.     case NFSPROC_SETATTR:
  750.         printf(" setattr");
  751.         dp = parserep(rp, length);
  752.         if (dp != 0 && parseattrstat(dp, !qflag) != 0)
  753.             return;
  754.         break;
  755.  
  756. #if NFSPROC_ROOT != NFSPROC_NOOP
  757.     case NFSPROC_ROOT:
  758.         printf(" root");
  759.         break;
  760. #endif
  761.     case NFSPROC_LOOKUP:
  762.         printf(" lookup");
  763.         dp = parserep(rp, length);
  764.         if (dp != 0 && parsediropres(dp) != 0)
  765.             return;
  766.         break;
  767.  
  768.     case NFSPROC_READLINK:
  769.         printf(" readlink");
  770.         dp = parserep(rp, length);
  771.         if (dp != 0 && parselinkres(dp) != 0)
  772.             return;
  773.         break;
  774.  
  775.     case NFSPROC_READ:
  776.         printf(" read");
  777.         dp = parserep(rp, length);
  778.         if (dp != 0 && parseattrstat(dp, vflag) != 0)
  779.             return;
  780.         break;
  781.  
  782. #if NFSPROC_WRITECACHE != NFSPROC_NOOP
  783.     case NFSPROC_WRITECACHE:
  784.         printf(" writecache");
  785.         break;
  786. #endif
  787.     case NFSPROC_WRITE:
  788.         printf(" write");
  789.         dp = parserep(rp, length);
  790.         if (dp != 0 && parseattrstat(dp, vflag) != 0)
  791.             return;
  792.         break;
  793.  
  794.     case NFSPROC_CREATE:
  795.         printf(" create");
  796.         dp = parserep(rp, length);
  797.         if (dp != 0 && parsediropres(dp) != 0)
  798.             return;
  799.         break;
  800.  
  801.     case NFSPROC_REMOVE:
  802.         printf(" remove");
  803.         dp = parserep(rp, length);
  804.         if (dp != 0 && parsestatus(dp) != 0)
  805.             return;
  806.         break;
  807.  
  808.     case NFSPROC_RENAME:
  809.         printf(" rename");
  810.         dp = parserep(rp, length);
  811.         if (dp != 0 && parsestatus(dp) != 0)
  812.             return;
  813.         break;
  814.  
  815.     case NFSPROC_LINK:
  816.         printf(" link");
  817.         dp = parserep(rp, length);
  818.         if (dp != 0 && parsestatus(dp) != 0)
  819.             return;
  820.         break;
  821.  
  822.     case NFSPROC_SYMLINK:
  823.         printf(" symlink");
  824.         dp = parserep(rp, length);
  825.         if (dp != 0 && parsestatus(dp) != 0)
  826.             return;
  827.         break;
  828.  
  829.     case NFSPROC_MKDIR:
  830.         printf(" mkdir");
  831.         dp = parserep(rp, length);
  832.         if (dp != 0 && parsediropres(dp) != 0)
  833.             return;
  834.         break;
  835.  
  836.     case NFSPROC_RMDIR:
  837.         printf(" rmdir");
  838.         dp = parserep(rp, length);
  839.         if (dp != 0 && parsestatus(dp) != 0)
  840.             return;
  841.         break;
  842.  
  843.     case NFSPROC_READDIR:
  844.         printf(" readdir");
  845.         dp = parserep(rp, length);
  846.         if (dp != 0 && parserddires(dp) != 0)
  847.             return;
  848.         break;
  849.  
  850.     case NFSPROC_STATFS:
  851.         printf(" statfs");
  852.         dp = parserep(rp, length);
  853.         if (dp != 0 && parsestatfs(dp) != 0)
  854.             return;
  855.         break;
  856.  
  857.     default:
  858.         printf(" proc-%lu", proc);
  859.         return;
  860.     }
  861.     fputs(" [|nfs]", stdout);
  862. }
  863.